package http;

import javax.xml.ws.http.HTTPException;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @ClassName SimpleHttpServer
 * @Description
 * @Author lichuanqi
 * @Date 2021/12/15 19:34
 * @Version 1.0
 **/
public class SimpleHttpServer {
    private int port;
    private HttpServlet servlet;
    private Selector selector;
    private ExecutorService executorService = Executors.newFixedThreadPool(5);
    public SimpleHttpServer(int port, HttpServlet servlet) throws IOException {
        this.port = port;
        this.servlet = servlet;
        Selector selector = Selector.open();
        this.selector = selector;
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public Thread start(){
        boolean run = true;
        Thread thread = new Thread(() -> {
            try {
                while(run){
                    dispatch();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        return thread;
    }

    public void dispatch() throws IOException {
        int select = selector.select();
        if (select==0){
            return;
        }
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()){
            SelectionKey next = iterator.next();
            iterator.remove();
            if (next.isAcceptable()){
                ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                SocketChannel socketChannel = channel.accept();
                System.out.println("已建立连接："+socketChannel.getRemoteAddress().toString());
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            }else if (next.isReadable()){
                SocketChannel channel = (SocketChannel) next.channel();
                System.out.println("开始读取数据");
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                while(channel.read(byteBuffer)>0){
                    byteBuffer.flip();
                    baos.write(byteBuffer.array(), 0, byteBuffer.remaining());
                   byteBuffer.clear();
                }

                if (baos.size()==0){
                    channel.close();
                }
                System.out.println("已读取数据");
                executorService.submit(()->{
                    try{
                        Request request = decode(baos.toByteArray());
                        Response response = new Response();
                        if ("GET".equalsIgnoreCase(request.method)) {
                            servlet.doGet(request, response);
                        }else if ("POST".equalsIgnoreCase(request.method)){
                            servlet.doPost(request, response);
                        }else {
                            throw new HTTPException(500);
                        }
                        writeBuffer.put(encode(response));
                        writeBuffer.flip();
                        channel.write(writeBuffer);
                        writeBuffer.clear();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                });
            }

        }
    }
    // 解码Http服务
    private Request decode(byte[] bytes) throws IOException {
        Request request = new Request();
        BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
        String firstLine = reader.readLine();
        System.out.println(firstLine);
        String[] split = firstLine.trim().split(" ");
        request.method = split[0];
        request.url = split[1];
        request.version = split[2];

        //读取请求头
        Map<String, String> heads = new HashMap<>();
        while (true) {
            String line = reader.readLine();
            if (line.trim().equals("")) {
                break;
            }
            String[] split1 = line.split(":");
            heads.put(split1[0], split1[1]);
        }
        request.heads = heads;
        request.params = getUrlParams(request.url);
        //读取请求体
        request.body = reader.readLine();
        return request;

    }

    private static Map getUrlParams(String url) {
        Map<String, String> map = new HashMap<>();
        url = url.replace("?", ";");
        if (!url.contains(";")) {
            return map;
        }
        if (url.split(";").length > 0) {
            String[] arr = url.split(";")[1].split("&");
            for (String s : arr) {
                if (s.contains("=")) {
                    String key = s.split("=")[0];
                    String value = s.split("=")[1];
                    map.put(key, value);
                }else {
                    map.put(s,null);
                }
            }
            return map;

        } else {
            return map;
        }
    }
    //编码Http 服务
    private byte[] encode(Response response) {
        StringBuilder builder = new StringBuilder(512);
        builder.append("HTTP/1.1 ")
                .append(response.code).append(Code.msg(response.code)).append("\r\n");

        if (response.body != null && response.body.length() != 0) {
            builder.append("Content-Length: ")
                    .append(response.body.length()).append("\r\n")
                    .append("Content-Type: text/html\r\n");
        }
        if (response.headers!=null) {
            String headStr = response.headers.entrySet().stream().map(e -> e.getKey() + ":" + e.getValue())
                    .collect(Collectors.joining("\r\n"));
            builder.append(headStr+"\r\n");
        }


//      builder.append ("Connection: close\r\n");// 执行完后关闭链接
        builder.append("\r\n").append(response.body);
        return builder.toString().getBytes();
    }
    public abstract static class HttpServlet {

        protected abstract void doGet(Request request, Response response);

        protected abstract void doPost(Request request, Response response);
    }

    public static class Request {
        Map<String, String> heads;
        String url;
        String method;
        String version;
        String body;    //请求内容
        Map<String, String> params;

        public Map<String, String> getHeads() {
            return heads;
        }

        public void setHeads(Map<String, String> heads) {
            this.heads = heads;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getMethod() {
            return method;
        }

        public void setMethod(String method) {
            this.method = method;
        }

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public String getBody() {
            return body;
        }

        public void setBody(String body) {
            this.body = body;
        }

        public Map<String, String> getParams() {
            return params;
        }

        public void setParams(Map<String, String> params) {
            this.params = params;
        }
    }

    public static class Response {
        Map<String, String> headers;
        int code;
        String body; //返回结果

        public Map<String, String> getHeaders() {
            return headers;
        }

        public void setHeaders(Map<String, String> headers) {
            this.headers = headers;
        }

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }

        public String getBody() {
            return body;
        }

        public void setBody(String body) {
            this.body = body;
        }
    }
}
